# -*- coding:utf-8 -*-
__author__ = 'Administrator'

class Descriptor(object):
    # def __get__(self, instance, owner):
    # __get__函数的参数意义如其名所示: instance表示调用的实例，如果是类调用则为None；owner为所属的类

    def __get__(self, obj, type):
        return 'get', self, obj, type

    def __set__(self, obj, value):
        return 'set', self, obj, value

    def __delete__(self, obj):
        return 'delete', self, obj


# python中，对象的方法也是可以认为是属性的
# 属性分为：Python自动产生的；自定义的；
# 类和实例对象都有__dict__属性，里面存放着它们自定义属性；
class T(object):
    name = 'name'

    def hello(self):
        print('hello')


t = T()
dir(t)

print(T.hello)
# <unbound method T.hello>，标示hello方法还没有和实例联系，仅为class的方法
print(t.hello)
# <bound method T.hello of <__main__.T object at 0x019750D0>>，表示hello已经和class的实例绑定。
# 那么问题，函数是如何从unbound------>bound进行切换的呢？
# 查找属性时，如：obj.attr。如果Python发现这个属性attr有个__get__方法，Python会调用attr的__get__方法，返回该
# 方法的返回值，而不是返回attr。
# 有一个descriptor的类(就像iterator一样)，其的特定方法有__get__,__set__,__delete__，其中后面两个是可选的。
# descriptor也必须依附对象，作为对象的一个属性，它也不能单独存在。
# descriptor必须存在于类的__dict__中。即只有在类的__dict__中找到属性，python才会看起有没有__get__方法，
#           但是对于一个实例的__dict__中找到的属性，python是不关注其__get__方法的，直接返回属性本身。

# 属性的查找策略，比如：obj.attr
# 1、如果attr是一个python自动产生的属性，找到（优先级最高）；
# 2、查找obj.__class__.__dict__，如果attr存在并且是data descriptor，返回data descriptor的__get__方法的结果；
#    如果没有继续在obj.__class__的父类以及祖先类中寻找data descriptor
# 3、在obj.__dict__中查找，这一步分两种情况，
#    第一种情况是obj是一个普通实例，找到就直接返回，找不到进行下一步。
#    第二种情况是obj是一个类，依次在obj和它的父类、祖先类的__dict__中查找，如果找到一个descriptor就返回descriptor的__get__方法的结果，
#             否则直接返回attr。如果没有找到，进行下一步
# 4、在obj.__class__.__dict__中查找，如果找到了一个descriptor(这里descriptor一定是non-data descriptor)
#    descriptor的__get__方法的结果。如果找到一个普通属性，直接返回属性值。如果没找到，进行下一步
# 5、到这一步，它raise AttributeError

# 赋值的查找策略，比如：obj.attr = value
# 1、查找obj.__class__.__dict__，如果attr存在并且是一个data descriptor，调用attr的__set__方法，结束。如果不存在，继续
# 2、到obj.__class__的父类和祖先类中查找，找到 data descriptor则调用其__set__方法。否则，继续；
# 3、直接在obj.__dict__中加入obj.__dict__['attr'] = value

